package com.atguigu.gulimall.product;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;

/**
 * 整合mybatis-plus
 * 1.导入依赖
 * 2.配置
 *  2.1 配置数据源
 *      2.1.1 配置数据库驱动
 *      2.1.2 在application.yml中配置数据源
 *  2.2 配置mybatis plus
 *      2.2.1 @MapperScan，告诉mapper接口在哪里
 *      2.2.2 告诉mybatis-plus sql映射文件位置
 *      2.2.3 配置主键生成规则
 *
 * 3.使用逻辑删除
 *      配置全局的逻辑删除规则，可以省略
 *      配置逻辑删除组件，3.1.1版本以上可以不配置
 *      加上逻辑删除注解 @TableLogic
 *
 * 4.JSR303数据校验
 *      给Bean需要校验的数据加上校验规则注解 javax.validation.constraints，并定义message提示
 *      告诉springMVC开启校验，添加校验注解 @Valid，校验错误以后会有默认的响应
 *      给校验的Bean后紧跟一个BindingResult，就能够拿到校验结果
 *
 *   分组校验
 *      给校验注解标注什么情况需要进行校验，指定分组 @Null(message = "新增不能指定id", groups = {AddGroup.class})
 *          分组为一个接口，不需要里面写内容
 *      controller上指定分组 @Validated({AddGroup.class})
 *      默认没有指定分组的校验注解，在分组校验情况下，不生效；只会在不分组校验的情况下生效
 *
 *   自定义校验
 *      编写一个自定义校验注解
 *      编写一个自定义的校验器 ConstraintValidator
 *      关联自定义校验注解和校验器
 *      @Documented
 *      @Constraint(
 *         validatedBy = {ListValueConstraintValidator.class}   可以指定多个不同的校验器，适配不同的校验
 *      )
 *      @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
 *      @Retention(RetentionPolicy.RUNTIME)
 *
 * 5.统一异常处理 @ControllerAdvice
 *
 * 6.模板引擎
 *      1.thymeleaf-starter：关闭缓存
 *      2.静态资源都放在static文件夹下就可以按照路径直接访问
 *      3.页面放在templates下，直接访问
 *      4.springboot访问项目的时候，默认会找index.html
 *      5.不重启服务器的情况下，页面修改事实更新
 *          引入devtools依赖
 *          修改完页面，使用ctrl+shift+f9重新编译即可
 *          如果是代码配置修改，推荐重启
 *
 * 7.整合redis
 *      1.引入redis-starter
 *      2.配置redis的host等信息
 *      3.使用springboot自动配置好的StringRedisTemplate来操作redis
 *
 * 8.整合redisson作为分布式锁的功能框架
 *      1.引入依赖
 *      2.配置redisson
 *
 * 9.整合SpringCache简化缓存开发
 *      1.引入依赖 spring-boot-starter-cache spring-boot-starter-data-redis
 *      2.写配置
 *          自动配置了哪些：
 *              CacheAutoConfiguration 会导入 RedisCacheConfiguration
 *              自动配置好了缓存管理器，RedisCacheManager
 *          配置使用redis作为缓存
 *              spring.cache.type=redis
 *          测试使用缓存
 *              @Cacheable：触发将数据保存到缓存的操作
 *              @CacheEvict：触发将数据从缓存删除的操作
 *              @CachePut：不影响方法执行更新缓存
 *              @Caching：组合以上多个操作
 *              @CacheConfig：在类级别共享缓存的相同配置
 *              1.开启缓存功能 @EnableCaching
 *              2.只需要使用注解就能完成缓存操作
 *      3.原理
 *          CacheAutoConfiguration -> RedisCacheConfiguration -> 自动配置了RedisCacheManager -> 初始化所有的缓存 ->
 *          每个缓存决定使用什么配置 -> 如果RedisCacheConfiguration有就用已有的，没有就用默认配置 ->
 *          想改缓存的配置，只需要给容器中放一个RedisCacheConfiguration即可，就会应用到当前RedisCacheManager管理的所有缓存分区中
 *      4.SpringCache的不足
 *          读模式：
 *              缓存穿透：查询一个null数据，解决 缓存空数据：spring.cache.redis.cache-null-values=true
 *              缓存雪崩：大量的key同时过期，解决方案：加随机时间/加过期时间
 *              缓存击穿：大量并发进来，同时查询一个正好过期的数据，解决方案：加锁。SpringCache默认是不加锁的
 *          写模式：缓存与数据库数据一致
 *              读写加锁
 *              引入Canal，感知到MySQL的更新去更新缓存
 *              读多写多：直接去数据库查询就行
 *
 *          总结：
 *              常规数据（读多写少，即时性，一致性要求不高的数据）完全可以使用SpringCache 写模式（ 只要缓存数据有过期时间就足够了）
 *              特殊数据：特殊设计
 */

@SpringBootApplication
@MapperScan(basePackages = "com.atguigu.gulimall.product.dao")
@EnableDiscoveryClient
@EnableFeignClients(basePackages = "com.atguigu.gulimall.product.feign")
@EnableRedisHttpSession
public class GulimallProductApplication {

    public static void main(String[] args) {
        SpringApplication.run(GulimallProductApplication.class, args);
    }

}
